package cn.janyd.action;

import cn.janyd.action.converter.DateType;
import cn.janyd.action.converter.IColumnType;
import cn.janyd.action.converter.MySqlTypeConverter;
import cn.janyd.action.converter.TypeConverter;
import cn.janyd.action.template.DataObjectClass;
import cn.janyd.action.template.MapperClass;
import cn.janyd.action.template.MapperXml;
import cn.janyd.action.utils.NameUtils;
import com.intellij.database.util.DasUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiModifier;
import lombok.Data;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.janyd.action.Constants.DATA_OBJECT_TEMPLATE;

/**
 * @author janyd
 * @since 2020/8/7 20:41
 */
@Data
@Accessors(chain = true)
public class ConfigBuilder {

    /**
     * lombok注解
     */
    private final static String DATA_ANNOTATION = "@Data";
    private final static String DATA_IMPORT = "lombok.Data";
    private final static String ACCESSORS_ANNOTATION = "@Accessors(chain = true)";
    private final static String ACCESSORS_IMPORT = "lombok.experimental.Accessors";

    /**
     * swagger注解
     */
    private final static String API_MODEL_ANNOTATION = "@ApiModel(value = \"%s\", description = \"%s\")";
    private final static String API_MODEL_IMPORT = "io.swagger.annotations.ApiModel";
    private final static String API_MODEL_PROPERTY_IMPORT = "io.swagger.annotations.ApiModelProperty";
    private final static String API_MODEL_PROPERTY_ANNOTATION = "@ApiModelProperty(value = \"%s\")";

    /**
     * MybatisPlus注解
     */
    private final static String TABLE_NAME_ANNOTATION = "@TableName(\"%s\")";
    private final static String TABLE_NAME_IMPORT = "com.baomidou.mybatisplus.annotation.TableName";
    private final static String TABLE_FIELD_ANNOTATION = "@TableField(\"%s\")";
    private final static String TABLE_FIELD_IMPORT = "com.baomidou.mybatisplus.annotation.TableField";

    private final static String PRIVATE = "private";
    private final static String PUBLIC = "public";
    private final static String PROTECTED = "protected";

    private static Map<String, String> templateMap = new HashMap<>(16);

    /**
     * 暂时支持Mysql
     */
    private TypeConverter typeConverter = new MySqlTypeConverter();

    private String dataObjectModulePath;

    private String dataObjectPackage;

    private String dataObjectSuffix;

    private String tablePrefix;

    private DateType dateType;

    private boolean enableSuperClass;

    private PsiClass superClass;

    private String mapperModulePath;

    private boolean enableMapper;

    private String mapperPackage;

    private boolean enableMapperXml;

    private String mapperXmlPath;

    private boolean lombok;

    private boolean lombokAccessors;

    private boolean swagger;

    private boolean mybatisPlusAnnotation;

    public ConfigBuilder init() {
//        this.commonFields = this.commonField();
        return this;
    }


    /**
     * 构建DataObject元数据
     *
     * @param tableInfo 表信息
     * @param date      日期
     * @param author    作者
     * @return DataObjectClass
     */
    public DataObjectClass buildDataObjectClass(TableInfo tableInfo, String date, String author) {
        String tableName = tableInfo.getTableName();
        if (StringUtils.isNotBlank(tablePrefix) && tableName.startsWith(tablePrefix)) {
            tableName = tableName.substring(tablePrefix.length());
        }
        String objectName = NameUtils.capitalFirst(NameUtils.underlineToCamel(tableName));
        String className = objectName;
        if (StringUtils.isNotBlank(dataObjectSuffix)) {
            className = className + dataObjectSuffix;
        }
        DataObjectClass.DataObjectClassBuilder builder = DataObjectClass.builder()
                .author(author)
                .date(date)
                .className(className)
                .objectName(objectName)
                .enableLombok(this.lombok)
                .lombokAccessors(lombokAccessors)
                .swagger(swagger)
                .mybatisPlus(mybatisPlusAnnotation)
                .packageName(this.dataObjectPackage)
                .tableName(tableInfo.getTableName())
                .comment(tableInfo.getTableComment());

        List<String> imports = new ArrayList<>();

        if (this.enableSuperClass) {
            builder.superClassName(this.superClass.getName());
            imports.add(this.superClass.getQualifiedName());
        }

        List<DataObjectClass.Field> commonFields = this.commonField();

        List<DataObjectClass.Field> fields = new ArrayList<>();


        tableInfo.getColumns().forEach(column -> {
            String name = NameUtils.underlineToCamel(column.getName());
            if (commonFields.stream().anyMatch(f -> Objects.equals(f.getName(), name))) {
                commonFields.stream().filter(f -> Objects.equals(f.getName(), name)).forEach(f -> {
                    IColumnType columnType = typeConverter.processTypeConvert(column.getDataType().typeName, this.dateType);
                    f.setPk(DasUtil.isPrimary(column))
                            .setModifier(PRIVATE)
                            .setComment(column.getComment())
                            .setType(columnType.getType())
                            .setCapitalName(NameUtils.capitalFirst(f.getName()))
                            .setColumnName(column.getName());
                });
                return;
            }

            DataObjectClass.Field field = new DataObjectClass.Field();
            field.setModifier(PRIVATE);
            IColumnType columnType = typeConverter.processTypeConvert(column.getDataType().typeName, this.dateType);
            if (!imports.contains(columnType.getPkg()) && columnType.getPkg() != null) {
                imports.add(columnType.getPkg());
            }

            field.setPk(DasUtil.isPrimary(column));

            field.setType(columnType.getType());
            field.setColumnName(column.getName());
            field.setName(name);
            field.setCapitalName(NameUtils.capitalFirst(field.getName()));
            field.setComment(column.getComment());
            fields.add(field);
        });

        if (fields.stream().anyMatch(DataObjectClass.Field::isPk) && this.mybatisPlusAnnotation) {
            imports.add("com.baomidou.mybatisplus.annotation.TableId");
        }
        builder.importPackages(imports)
                .fields(fields)
                .commonFields(commonFields)
                .templatePath(DATA_OBJECT_TEMPLATE);

        return builder.build();
    }

    /**
     * 构建Mapper元数据
     *
     * @param dataObjectClass DataObject
     * @param date            日期
     * @param author          作者
     * @return Mapper元数据
     */
    public MapperClass buildMapperClass(DataObjectClass dataObjectClass, String date, String author) {
        return MapperClass.builder()
                .author(author)
                .date(date)
                .packageName(this.mapperPackage)
                .dataObjectPackage(dataObjectClass.getPackageName())
                .dataObjectName(dataObjectClass.getClassName())
                .comment(dataObjectClass.getComment())
                .mapperName(dataObjectClass.getObjectName() + Constants.MAPPER_SUFFIX)
                .superMapperClass(Constants.BASE_MAPPER)
                .superMapperClassPackage(Constants.BASE_MAPPER_CLASS)
                .build();
    }

    /**
     * 构建MapperXml
     *
     * @param dataObjectClass DataObject
     * @param mapperClass     MapperClass
     * @return
     */
    public MapperXml buildMapperXml(DataObjectClass dataObjectClass, MapperClass mapperClass) {
        List<String> fieldNames = new ArrayList<>();

        fieldNames.addAll(dataObjectClass.getCommonFields().stream().map(DataObjectClass.Field::getColumnName).collect(Collectors.toList()));
        fieldNames.addAll(dataObjectClass.getFields().stream().map(DataObjectClass.Field::getColumnName).collect(Collectors.toList()));

        return MapperXml.builder()
                .baseResultMap(true)
                .commonFields(dataObjectClass.getCommonFields())
                .fields(dataObjectClass.getFields())
                .mapperPackageName(mapperPackage)
                .mapperName(mapperClass.getMapperName())
                .dataObjectPackage(dataObjectClass.getPackageName())
                .dataObjectName(dataObjectClass.getClassName())
                .fieldNames(String.join(", ", fieldNames))
                .build();
    }

    private List<DataObjectClass.Field> commonField() {
        if (superClass == null) {
            return Collections.emptyList();
        }
        final @NotNull PsiField[] allFields = superClass.getFields();
        return Stream.of(allFields)
                .filter(f -> {
                    if (f.getModifierList() != null) {
                        return !f.getModifierList().hasModifierProperty(PsiModifier.STATIC);
                    }
                    return true;
                }).map(f -> new DataObjectClass.Field()
                        .setName(f.getName())
                        .setColumnName(NameUtils.camelToUnderline(f.getName()))
                        .setCapitalName(NameUtils.capitalFirst(NameUtils.camelToUnderline(f.getName()))))
                .collect(Collectors.toList());
    }


}
